/* project:			package:		class:			credits:					date:
 * GuildMaster		fileIO			SkinIO			by Dolraith & Kirenno		Aug 2011
 * -- reads skin files and creates a skins accordingly
 * 
 * === PUBLIC FNs ===================================================================
 * 	public SkinIO ()	-- constructor
 *  public Skin buildSkin ()					-- returns the skin object build according to default skin file (this.defaultSkin)
 * 	public Skin buildSkin (String skinPath)		-- returns the skin object build according to file specified by skinPath
 *  
 */

package fileIO;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedList;

import skin.Skin;
import tools.StringTools;

public class SkinIO {
	// === GLOBAL CLASS VARIABLES =================================================================
	String defaultSkin = "config/default.skin";		// skin to load if specified skin is not found
//	boolean useXML = false;							// switch variable for input data style
	
	// === CONSTRUCTOR, INITIALIZERS & resets =====================================================
	public SkinIO () {	}
	
	// === OBJECT BUILDERS ========================================================================
	public Skin buildSkin () {					// builds default skin
		return buildSkin (defaultSkin);
	}
	public Skin buildSkin (String skinPath) {	// builds skin according to file at found skinPath
		Skin skin = new Skin ();
		readSkinFile (skinPath, skin);
		return skin;
	}
	
	// === IO METHODS & parsers ===================================================================
	private void readSkinFile (String skinPath, Skin skin) {	// reads the file at skinPath and passes values into skin
		try {
			boolean useXML = false;							// by default assume skin file is not in XML format
			String xml_cache = "";							// temp cache string for use with xml reading
			boolean insideComment = false;					// switch variable to keep track of tag-style comments
			LinkedList <String> openTags = new LinkedList <String> ();	// list of tags to keep track of where we're at in xml
			
			BufferedReader reader = new BufferedReader (new FileReader(skinPath));
			String curLine; 								// this will be where the current line will be stored
			
			boolean atFileHeader = true;
			while((curLine = reader.readLine()) != null) 	//while there's something left of the config, keep reading it
			{
				curLine = StringTools.clearComments(curLine).trim();	// drop comments & trim
				if (curLine.isEmpty()) continue;						// skip empty lines
				
				if (atFileHeader && curLine.startsWith("<") && curLine.contains(">")) {
					useXML = true;								// if first line of non-comment content looks like tags,
				}												// read this file as XML
	
				if (!useXML) {									// if no xml is detected, skip a line
					Skin.reportSkinError("skinIO.readSkinFile", "Expected xml tag structure. Skipping line. (line = \"" + curLine + "\")");
					continue;
					//enforce(curLine, skin);					// parse like options
				} else {											// ----- else attempt to parse xml
					
					
															// process tag-style comments
					if (insideComment) {							// if you know this has been a comment
						if (!curLine.contains("-->")) continue;		// and the line contains no comment end, skip this line
						else {										// if comment end-token exists
							curLine = curLine.substring(curLine.indexOf("-->")+3);	// drop comment tail
							insideComment = false;									// remember you're no longer in-comment
						}
					}
					while ((curLine.contains("<!--")) && (curLine.contains("-->"))) {// drop full comments from line
						curLine = curLine.substring(0,curLine.indexOf("<!--")).concat(curLine.substring(curLine.indexOf("-->")+3));
					}
					if (curLine.contains("<!--")) {									// if comment is only starting
						curLine = curLine.substring(0,curLine.indexOf("<!--"));			// drop the beggining comment
						insideComment = true;											// and remember we're commenting
					}
					
															// process XML tags
					while (curLine.contains("<") && curLine.contains(">") && (curLine.indexOf("<") < curLine.indexOf(">"))) 
					{												// while current line contains tags
						String tag = "";
						xml_cache = xml_cache.concat(curLine.substring(0,curLine.indexOf("<")));	// cache whats before tag
						curLine = curLine.substring(curLine.indexOf("<"));
						tag = curLine.substring (1,curLine.indexOf(">"));
						curLine = curLine.substring(curLine.indexOf(">")+1);
						if (tag.startsWith("/")) {					// if this is a closing tag
							tag = tag.substring(1);						// drop the closing mark
							if (openTags.getLast().equals(tag)) {			// if this closing tag matches last open tag
								openTags.removeLast();							// and remove tag from openTag list
								skin.parseInput (openTags, tag, xml_cache);		// process the contents
								xml_cache = "";									// clear cache
							} else {										// if this closing tag doesnt match last open tag
								Skin.reportSkinError("XML tag structure", "the openings and closing tags dont match up (tag = <".concat(tag).concat(">"));
								while (!openTags.isEmpty()) {
									if (openTags.getLast().equals(tag)) {			// if this closing tag matches last open tag
										openTags.removeLast();							// and remove tag from openTag list
										skin.parseInput (openTags, tag, xml_cache);		// process the contents
										xml_cache = "";									// clear cache
									} else {										// if the tags dont match
										xml_cache = "<".concat(openTags.getLast()).concat(">").concat(xml_cache);// append it to cache as contents
										openTags.removeLast();
									}
								}
								if (!xml_cache.isEmpty()) {							// if this closing tag has no match open at all
									xml_cache = xml_cache.concat("</").concat(tag).concat(">");	// skip it as part of contents
								}
							}
						} else {									// else if this is an opening tag
							openTags.add(tag);							// remember we opened this
							xml_cache = "";								// make sure cache is empty
						}
					} 
					xml_cache = xml_cache.concat(curLine);			// when current line contains no tags, add it to cache
				}													// ----- end of XML read
				
				
				
				atFileHeader = false;								// remember we're not on first line of content anymore
			}
			reader.close();
		} catch (FileNotFoundException e) {
			if (skinPath.equalsIgnoreCase(defaultSkin)) {	// if we were looking for default skin and failed to find it
				// TODO generate no-skin look
				Skin.reportSkinError("skin file", "default skin file not found (path = ".concat(skinPath).concat(")"));
				e.printStackTrace();
			} else {										// if we were looking for other skins and failed to find it, use default
				Skin.reportSkinError("skin file", "file not found (path = ".concat(skinPath).concat("); loading default skin"));
				readSkinFile (defaultSkin, skin);
			}
		} catch (IOException e) {
			//TODO: WTF causes an IO exception anyway? corrupted file? I guess here we regen config from a backup or soemthing
			Skin.reportSkinError("skin file", "file IO exception (path = ".concat(skinPath).concat(")"));
			e.printStackTrace();
		}
	}
//	private void enforce(String rawCfg, Skin skin) 	{			
//		String[] parsedCfg = FileUtil.parse(rawCfg); 	// 0 will always have the config name, everything after, the vals
//		if (!parsedCfg[0].isEmpty()) skin.parseInput (parsedCfg);
//	}
	
}
